1 package edu.jiangxin.apktoolbox.file.password.recovery.category.dictionary.multithread; 2 3 import edu.jiangxin.apktoolbox.file.core.EncoderDetector; 4 import edu.jiangxin.apktoolbox.file.password.recovery.RecoveryPanel; 5 import edu.jiangxin.apktoolbox.file.password.recovery.State; 6 import org.apache.logging.log4j.LogManager; 7 import org.apache.logging.log4j.Logger; 8 9 import java.io.*; 10 import java.nio.MappedByteBuffer; 11 import java.nio.channels.FileChannel; 12 import java.util.HashSet; 13 import java.util.Set; 14 import java.util.concurrent.BrokenBarrierException; 15 import java.util.concurrent.CyclicBarrier; 16 import java.util.concurrent.ScheduledThreadPoolExecutor; 17 import java.util.concurrent.atomic.AtomicBoolean; 18 import java.util.concurrent.atomic.AtomicInteger; 19 20 public class BigFileReader { 21 private static final Logger logger = LogManager.getLogger(BigFileReader.class.getSimpleName()); 22 23 private static final int DEFAULT_BUFFER_SIZE = 1024 * 1024; 24 25 private static final int PROCESSOR_COUNT = Runtime.getRuntime().availableProcessors(); 26 27 private final String charset; 28 private final int bufferSize; 29 private final ScheduledThreadPoolExecutor executorService; 30 private final long fileLength; 31 private RandomAccessFile rAccessFile; 32 private final Set<StartEndPair> startEndPairs; 33 private CyclicBarrier cyclicBarrier; 34 private final AtomicInteger counter = new AtomicInteger(0); 35 private final CompleteCallback callback; 36 37 private final AtomicBoolean success = new AtomicBoolean(false); 38 39 private final RecoveryPanel panel; 40 41 public BigFileReader(CompleteCallback callback, RecoveryPanel panel) { 42 this.panel = panel; 43 File file = panel.getDictionaryFile(); 44 if (!file.exists()) { 45 throw new IllegalArgumentException("文件不存在!"); 46 } 47 this.fileLength = file.length(); 48 this.charset = EncoderDetector.judgeFile(file.getAbsolutePath()); 49 this.bufferSize = DEFAULT_BUFFER_SIZE; 50 try { 51 this.rAccessFile = new RandomAccessFile(file, "r"); 52 } catch (FileNotFoundException e) { 53 logger.error("BigFileReader FileNotFoundException"); 54 } 55 this.executorService = new ScheduledThreadPoolExecutor(PROCESSOR_COUNT); 56 this.startEndPairs = new HashSet<>(); 57 this.callback = callback; 58 } 59 60 public void start() { 61 long everySize = fileLength / PROCESSOR_COUNT; 62 try { 63 calculateStartEnd(0, everySize); 64 } catch (IOException e) { 65 logger.error("start", e); 66 return; 67 } 68 69 final long startTime = System.currentTimeMillis(); 70 int parties = startEndPairs.size(); 71 logger.info("[TaskTracing]Parties: " + parties); 72 cyclicBarrier = new CyclicBarrier(parties, () -> { 73 logger.info("use time: " + (System.currentTimeMillis() - startTime) + "ms"); 74 logger.info("all line: " + counter.get()); 75 callback.onComplete(null); 76 }); 77 for (StartEndPair pair : startEndPairs) { 78 logger.info("pair: " + pair); 79 executorService.execute(new SliceReaderTask(pair)); 80 } 81 } 82 83 private void calculateStartEnd(long start, long size) throws IOException { 84 if (start > fileLength - 1) { 85 return; 86 } 87 StartEndPair pair = new StartEndPair(); 88 pair.start = start; 89 long endPosition = start + size - 1; 90 if (endPosition >= fileLength - 1) { 91 pair.end = fileLength - 1; 92 startEndPairs.add(pair); 93 return; 94 } 95 96 rAccessFile.seek(endPosition); 97 byte tmp = (byte) rAccessFile.read(); 98 while (tmp != '\n' && tmp != '\r') { 99 endPosition++; 100 if (endPosition >= fileLength - 1) { 101 endPosition = fileLength - 1; 102 break; 103 } 104 rAccessFile.seek(endPosition); 105 tmp = (byte) rAccessFile.read(); 106 } 107 pair.end = endPosition; 108 startEndPairs.add(pair); 109 110 calculateStartEnd(endPosition + 1, size); 111 } 112 113 public void shutdown() { 114 try { 115 rAccessFile.close(); 116 } catch (IOException e) { 117 logger.error("shutdown IOException"); 118 } 119 executorService.shutdown(); 120 logger.info("shutdown executorService"); 121 } 122 123 private void handle(byte[] bytes) throws UnsupportedEncodingException { 124 if (success.compareAndSet(true, true) || panel.getCurrentState() != State.WORKING) { 125 return; 126 } 127 128 String line; 129 if (charset == null) { 130 line = new String(bytes); 131 } else { 132 line = new String(bytes, charset); 133 } 134 135 panel.setCurrentPassword(line); 136 panel.increaseProgressBarValue(); 137 counter.decrementAndGet(); 138 139 if (panel.getCurrentFileChecker().checkPassword(line)) { 140 if (success.compareAndSet(false, true)) { 141 logger.info("find password: {}", line); 142 callback.onComplete(line); 143 } 144 } else { 145 if (!success.compareAndSet(true, true) && panel.getCurrentState() == State.WORKING) { 146 logger.info("try password[{}] failed", line); 147 } 148 } 149 } 150 151 private static class StartEndPair { 152 public long start; 153 public long end; 154 155 @Override 156 public String toString() { 157 return "star=" + start + ";end=" + end; 158 } 159 160 @Override 161 public int hashCode() { 162 final int prime = 31; 163 int result = 1; 164 result = prime * result + (int) (end ^ (end >>> 32)); 165 result = prime * result + (int) (start ^ (start >>> 32)); 166 return result; 167 } 168 169 @Override 170 public boolean equals(Object obj) { 171 if (this == obj) 172 return true; 173 if (obj == null) 174 return false; 175 if (getClass() != obj.getClass()) 176 return false; 177 StartEndPair other = (StartEndPair) obj; 178 if (end != other.end) 179 return false; 180 return start == other.start; 181 } 182 183 } 184 185 private class SliceReaderTask implements Runnable { 186 private final long start; 187 private final long sliceSize; 188 private final byte[] readBuff; 189 190 public SliceReaderTask(StartEndPair pair) { 191 this.start = pair.start; 192 this.sliceSize = pair.end - pair.start + 1; 193 this.readBuff = new byte[bufferSize]; 194 } 195 196 @Override 197 public void run() { 198 try { 199 MappedByteBuffer mapBuffer = rAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, start, this.sliceSize); 200 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 201 for (int offset = 0; offset < sliceSize; offset += bufferSize) { 202 int readLength; 203 if (offset + bufferSize <= sliceSize) { 204 readLength = bufferSize; 205 } else { 206 readLength = (int) (sliceSize - offset); 207 } 208 mapBuffer.get(readBuff, 0, readLength); 209 for (int i = 0; i < readLength; i++) { 210 byte tmp = readBuff[i]; 211 if (tmp == '\n' || tmp == '\r') { 212 if (bos.size() > 0) { 213 handle(bos.toByteArray()); 214 } 215 bos.reset(); 216 } else { 217 bos.write(tmp); 218 } 219 } 220 } 221 if (bos.size() > 0) { 222 handle(bos.toByteArray()); 223 } 224 logger.info("[TaskTracing]Waiting number: " + cyclicBarrier.getNumberWaiting()); 225 } catch (Exception e) { 226 logger.error("run Exception" + e.getMessage()); 227 } 228 try { 229 cyclicBarrier.await(); 230 } catch (InterruptedException e) { 231 logger.error("await InterruptedException"); 232 Thread.currentThread().interrupt(); 233 } catch (BrokenBarrierException e) { 234 logger.error("await BrokenBarrierException"); 235 } 236 } 237 238 } 239 }